{
  // entries
  Object.prototype._entries = function (obi) {
    const arr = [];
    for (const key in obi) {
      if (obj.hasOwnProperty(key)) {
        arr.push([[key, obj[key]]]);
      }
    }

    return arr;
  };
}

{
  // fromEntries
  // 跟entries相反，将键值对数组转成对象
  Object.prototype._fromEntries = function (arr) {
    const res = {};

    for (let i = 0; i < arr.length; i++) {
      const [key, value] = arr[i];
      res[key] = value;
    }

    return res;
  };
}

{
  // keys
  Object.prototype._keys = function (obi) {
    const arr = [];
    for (const key in obi) {
      if (obj.hasOwnProperty(key)) {
        arr.push(key);
      }
    }

    return arr;
  };
}

{
  // values
  Object.prototype._values = function (obi) {
    const arr = [];
    for (const key in obi) {
      if (obj.hasOwnProperty(key)) {
        arr.push(obj[key]);
      }
    }

    return arr;
  };
}

{
  // instanceof

  // instanceof主要用于判断某个实例是否属于某个类型，或者判断某个实例是其父类型或祖先类型
  // 实现原理就是只要右边变量的prototype在左边变量的原型链上即可。
  // 因此instanceof在查找过程会遍历左边变量的原型链，直到找到右边变量的prototype；
  // 如果查找失败，则返回false

  function _instanceof(left, right) {
    let leftVal = left.__proto__;
    let rightVal = right.prototype;

    // 若找不到，就一直循环父类或祖先
    while (leftVal) {
      // 查找到原型
      if (leftVal === rightVal) {
        return true;
      }
      // 获取祖先实例
      leftVal = leftVal.__proto__;
    }

    return false;
  }
}

{
  // is
  Object.prototype._is = function (x, y) {
    if (x === y) {
      // 防止 -0 和 +0
      return x !== 0 || 1 / x === 1 / y;
    }

    // 防止 NaN
    return x !== x && y !== y;
  };
}

{
  // assign
  Object.prototype._assign = function (target, ...args) {
    if (target === null || target === undefined) {
      throw new TypeError('Cannot convert undefined or null to object');
    }

    var to = Object(target);

    for (const nextSource of args) {
      for (const key in nextSource) {
        if (nextSource.hasOwnProperty(key)) {
          to[key] = nextSource[key];
        }
      }
    }

    return to;
  };
}

{
  // freeze
  const obj = {
    name: 'yd',
    info: {
      address: 'beijing',
    },
  };
  const freezeObj = Object.freeze(obj);
  freezeObj.name = 'new name';
  console.log(freezeObj.name);

  function _freeze(obj) {
    if (obj instanceof Object) {
      for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
          Object.defineProperty(obj, key, {
            writable: false, // 设置只读
          });
          Object.seal(obj);
        }
      }
    }
    return obj;
  }
  const freezeObj1 = Object.freeze(obj);
  freezeObj1.name = 'new name';
  console.log(freezeObj1.name);
}

{
  // 原生实现 es5 的 Object.create()方法
  // Object.create(proto[, propertiesObject])
  // Object.create()方法使用指定的原型对象和其属性创建了一个新的对象。
  // propertiesObject 对应 Object.defineProperties() 的第二个参数，表示给新创建的对象的属性设置描述符。
  // 如果 propertiesObject 参数是 null 或非原始包装对象，则抛出一个 TypeError 异常.

  // 法一 简单粗暴
  Object.create = function (obj) {
    // 创建一个空的构造函数
    function F() {}
    // 将构造函数的 prototype 指向传入的对象
    F.prototype = obj;
    // 返回新的构造函数
    return new F();
  };

  // 法二 实现propertiesObject参数
  Object.create = function (prototype, properties) {
    if (typeof prototype !== 'object') {
      throw TypeError();
    }
    function Ctor() {}
    Ctor.prototype = prototype;
    var o = new Ctor();
    if (prototype) {
      o.constructor = Ctor;
    }
    if (properties !== undefined) {
      if (properties !== Object(properties)) {
        throw TypeError();
      }
      Object.defineProperties(o, properties);
    }
    return o;
  };
}
